﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DimOS.Constants;
using DimOS.DimSH;


namespace DimOS.Plugs
{
    public static class String
    {
        public static byte[] Encode(this string input)
        { var output = new byte[input.Length]; for (int i = 0; i < input
          .Length; i++) output[i] = (byte)input[i]; return output; }

        public static string Decode(this byte[] input)
        { var output = ""; for (int i = 0; i < input.Length; i++)
          output += (char)input[i]; return output; }

        public static bool Matches(this string text, string pattern)
        {
            if (text.Length + pattern.Length == 0) return true; //Nothing left to evaluate

            //Ensure that the pattern's part after WildcardAny(*) exists in the text string
            if (pattern.Length > 1 && pattern[0] == Tokens.WildcardAny && text.Length == 0)
                return false;

            //Check if text's chararacter matches the character or WildcardOne(?) in the pattern
            if ((pattern.Length > 1 && pattern[0] == Tokens.WildcardOne) || (pattern.Length != 0 &&
                text.Length != 0 && (pattern[0] == text[0] || pattern[0] == Tokens.WildcardOne)))
                return Matches(text.Substring(1), pattern.Substring(1));

            //Either evaluate or skip the text's character if WildcardAny(*) was met
            if (pattern.Length != 0 && pattern[0] == Tokens.WildcardAny) return
                Matches(text, pattern.Substring(1)) || Matches(text.Substring(1), pattern);

            return false;
        }

        public static bool StartsWith(string source, string target)
        { return IndexOf(source, target) == 0; }

        public static bool EndsWith(string source, string target)
        { if (target.Length > source.Length) return false; return source
          .Substring(source.Length - target.Length) == target; }

        public static string Replace(string text, string from, string to, bool skipMirrored = false)
        {
            var result = ""; do { var index = IndexOf(text, from); if (index == -1) break; result
            += (index != 0 ? text.Substring(0, index + (skipMirrored && (text[index - 1] == Tokens
            .MirrorLeft || text[index - 1] == Tokens.MirrorRight) ? from.Length : 0)) : "") + to;
            text = text.Substring(index + from.Length); } while (text.Length != 0); return result
            + text;
        }

        public static int IndexOf(string text, string substring)
        {
            for (int i = 0; i < text.Length; i++) { bool found = true; for (int j = 0; j < substring
            .Length; j++) { if (text[i + j] != substring[j]) { found = false; break; } } if (found)
            return i; } return -1;
        }

        public static int IndexOfAny(string text, char[] characters, int start = 0)
        {
            var min = -1; for (int i = 0; i < characters.Length; i++)
            { var index = text.IndexOf(characters[i], start); if (index
            != -1 && (index < min || min == -1)) min = index; } return min;
        }
        //...

        public static string Unmirror(this string text, char from)
        {
            var result = text[0].ToString();
            for (int i = 1; i < text.Length; i++)
                result = text[i] == from && (text[i - i] == Tokens.MirrorLeft || text[i - 1] == Tokens.MirrorRight)
                    ? result.Remove(result.Length - 1) + from : result + text[i];
            return result;
        }

        public static string Mirror(this string text, char to)
        {
            var result = ""; for (int i = 0; i < text.Length; i++) result += text[i] ==
            to ? Tokens.MirrorRight + to.ToString() : text[i].ToString(); return result;
        }

        public static string AsByte(this string text)
        { var result = Convert.DecimalToBase(text, 16).PadLeft(2, '0');
          return result.Length > 2 ? result.Remove(0, result.Length - 2) : result; }

        //Trims the starting and trailing spaces from the string
        public static string Trim(string text, char splitter = Tokens.Space)
        {
            var result = text;
            var length = text.Length;
            if (length != 0)
            {
                var trimmingLength = 0;
                for (int i = 0; i < length; i++)
                {
                    if (text[i] == splitter) trimmingLength++;
                    else break;
                }
                result = result.Remove(0, trimmingLength);
                trimmingLength = 0;
                if (result.Length != 0)
                {
                    for (int i = length - 1; i >= 0; i--)
                    {
                        if (text[i] == splitter) trimmingLength++;
                        else break;
                    }
                    result = result.Remove(result.Length - trimmingLength, trimmingLength);
                }
            }
            return result;
        }

        public static string TrimStart(string text, char splitter = Tokens.Space)
        {
            var result = text;
            var length = text.Length;
            if (length != 0)
            {
                var trimmingLength = 0;
                for (int i = 0; i < length; i++)
                {
                    if (text[i] == splitter) trimmingLength++;
                    else break;
                }
                result = result.Remove(0, trimmingLength);
            }
            return result;
        }

        public static string TrimEnd(string text, char splitter = Tokens.Space)
        {
            var result = text;
            var length = text.Length;
            if (length != 0)
            {
                var trimmingLength = 0;
                if (result.Length != 0)
                {
                    for (int i = length - 1; i >= 0; i--)
                    {
                        if (text[i] == splitter) trimmingLength++;
                        else break;
                    }
                    result = result.Remove(result.Length - trimmingLength, trimmingLength);
                }
            }
            return result;
        }

        public static string Truncate(this string number, int decimalPlaces, bool countZeroes = false)
        {
            var result = "";
            var trigger = -2;
            for (int i = 0; i < number.Length; i++)
            {
                if ((number[i] == '.' || number[i] == ',') ||
                    (trigger == -1 && (countZeroes ||
                    number[i] != '0')) || (trigger >= 0))
                    trigger ++;

                if (trigger == decimalPlaces) break;
                result += number[i];
            }
            return result;
        } 

        public static int CompareNaturally(string source, string target) 
        { 
            if (source != null && target != null && (source = Replace(source, " ",
                "")).Length > 0 && (target = Replace(target, " ", "")).Length > 0) 
            { 
                int sourcePointer = 0, targetPointer = 0; 
                while (sourcePointer < source.Length) 
                { 
                    if (targetPointer >= target.Length) return 1; 
  
                    if (Char.IsDigit(source[sourcePointer])) 
                    { 
                        if (!Char.IsDigit(target[targetPointer])) return -1; 
                        string sourceChunk = "", targetChunk = "";
                        int sourceNumber, targetNumber;

                        while (sourcePointer < source.Length &&
                            Char.IsDigit(source[sourcePointer])) 
                            sourceChunk += source[sourcePointer++]; 
  
                        while (targetPointer < target.Length &&
                            Char.IsDigit(target[targetPointer])) 
                            targetChunk += target[targetPointer++]; 
  
                        try { sourceNumber = Convert.ToInt32(sourceChunk); }
                        catch (OverflowException) { sourceNumber = Int32.MaxValue; }
  
                        try { targetNumber = Convert.ToInt32(targetChunk); }
                        catch (OverflowException) { targetNumber = Int32.MaxValue; }
  
                        if (sourceNumber < targetNumber) return -1;
                        if (sourceNumber > targetNumber) return 1;
                    }
                    else if (Char.IsDigit(target[targetPointer])) return 1;
                    else
                    { int difference = string.CompareOrdinal(source[sourcePointer].ToString(),
                      target[targetPointer].ToString()); if (difference > 0) return 1;
                      if (difference < 0) return -1; sourcePointer++; targetPointer++; }
                } if (targetPointer < target.Length) return -1;
            } return 0;
        }



        public static List<string> Split(string source, string separator, bool removeEmpty = false)
        { var result = new List<string>(); if (string.IsNullOrEmpty(separator)) { result.Add(source);
          return result; } var index = IndexOf(source, separator); if (index == -1) { result.Add(source);
          return result; } while ((index = IndexOf(source, separator)) != -1) { var temp = source.Substring
          (0, index); if (temp != "" || !removeEmpty) result.Add(temp); source = source.Remove(0, index +
          separator.Length); } if (source != "" || !removeEmpty) result.Add(source); return result; }

        public static string Increment(this string text)
        {
            if (Trim(text) == "") return text;

            var temp = text.ToCharArray(); for (int i = temp.Length - 1; i >= 0; i--) {
            if ((temp[i] >= '0' && temp[i] <= '8') || (temp[i] >= 'A' && temp[i] <= 'Y')
            || (temp[i] >= 'a' && temp[i] <= 'y')) { temp[i] = (System.Char)(temp[i] + 1);
            if (i < temp.Length - 1 && temp[i + 1] == 'Z' || temp[i + 1] == 'z') temp[i +
            1] = '0'; break; } if (temp[i] == '9') { temp[i] = 'A'; break; } if (i == 0 &&
            (temp[i] == 'Z' || temp[i] == 'z')) { for (int j = 0; j < temp.Length; j++) if
            (temp[j] == 'Z' || temp[j] == 'z') temp[j] = '0'; return new string(temp) + '0';
            } } return new string(temp);
        }

        public static string Decrement(this string text)
        {
            if (Trim(text) == "") return text;

            var temp = text.ToCharArray(); for (int i = temp.Length - 1; i >= 0; i--) { if
            ((temp[i] >= '1' && temp[i] <= '9') || (temp[i] >= 'B' && temp[i] <= 'Z') ||
            (temp[i] >= 'b' && temp[i] <= 'z')) { temp[i] = (System.Char)(temp[i] - 1); if
            (i < temp.Length - 1 && temp[i + 1] == '0') temp[i + 1] = 'Z'; break; } if (temp
            [i] == 'A' || temp[i] == 'a') { temp[i] = '9'; break; } if (i == 0 && temp[i] ==
            '0') { for (int j = 0; j < temp.Length; j++) if (temp[j] == '0') temp[j] = 'Z';
            return new string(temp).Remove(temp.Length - 1, 1); } } return new string(temp);
        }

        public static string Reverse(this string text)
        {
            var result = text.ToCharArray(); for (int i = 0; i < text.Length / 2; i++)
            { var temp = result[i]; result[i] = result[result.Length - i - 1]; result
            [result.Length - i - 1] = temp; } return new string(result); 
        }

        public static string Sort(this string text)
        { var result = text.ToCharArray(); Sort(ref result,
          0, result.Length - 1); return new string(result); }

        public static void Sort(ref char[] list, int start, int end)
        {
            var pivot = list[(end - start) / 2 + start]; int i = start, j = end;
            while (i <= j)
            {
                while (list[i] < pivot && i <= end) ++i;
                while (list[j] > pivot && j >= start) --j;
                if (i <= j) { var temp = list[i]; list[i] = list[j]; list[j] = temp; ++i; --j; }
            }
            if (j > start) Sort(ref list, start, j); if (i < end) Sort(ref list, i, end);
        }
    }
}
